package juc.thread;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

class NotifyTest {

    private final Object object = new Object();
    private List<Integer> sleepList = new LinkedList<>();
    private List<Integer> notifyList = new LinkedList<>();

    public void startThread(int i) {
        new Thread(new Runnable() { // 启动一个线程
            @Override
            public void run() {
                synchronized (object) {
                    try {
                        // 加入休眠队列后休眠
                        sleepList.add(i);
                        object.wait();
                        // 唤醒后加入唤醒队列
                        notifyList.add(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    public static void testNotify() throws InterruptedException {
        NotifyTest a = new NotifyTest();
        for(int i = 1; i < 30; i++){
            // 启动 30 个线程，30 个线程将都进入阻塞状态
            a.startThread(i);
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println();
        for(int i = 1; i < 30; i++){
            //Thread.sleep(10);
            synchronized (a.object) {
                // 一次唤醒一个线程，唤醒30 次，每次被唤醒的线程都会继续执行代码加入唤醒对象
                a.object.notify();
            }
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 如果休眠顺序和唤醒顺序一样，那么将证明是先阻塞先唤醒
        System.out.println("休眠顺序" + a.sleepList);
        System.out.println("唤醒顺序" + a.notifyList);
    }

}


class SignalTest {
   private static ReentrantLock lock = new ReentrantLock();
   private static Condition condition = lock.newCondition();
   private List<Integer> sleepList = new LinkedList<>();
   private List<Integer> notifyList = new LinkedList<>();

   public void startThread(int i) {
       new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   lock.lock();
                   // 加入休眠队列后休眠
                   sleepList.add(i);
                   condition.await();
                   // 唤醒后加入唤醒队列
                   notifyList.add(i);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               } finally {
                   lock.unlock();
               }
           }
       }).start();
   }

   public static void testSingal() throws InterruptedException {
       SignalTest a = new SignalTest();
       for(int i = 1; i < 30; i++){
           // 启动 30 个线程，30 个线程将都进入阻塞状态
           a.startThread(i);
       }
       try {
           Thread.sleep(1000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println();

       for(int i = 1; i < 30; i++){
           Thread.sleep(10);
           lock.lock();
           // 一次唤醒一个线程，唤醒30 次，每次被唤醒的线程都会继续执行代码加入唤醒对象
           condition.signal();  // signal() 需要获得lock
           lock.unlock();
       }

       try {
           Thread.sleep(1000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       // 如果休眠顺序和唤醒顺序一样，那么将证明是先阻塞先唤醒
       System.out.println("休眠顺序" + a.sleepList);
       System.out.println("唤醒顺序" + a.notifyList);
   }

}




/**
 * @ClassName : 验证是否是公平唤醒
 * @Author : hyy
 * @Date: 2020-03-13
 * 用来验证 notify 和 singal是否是公平唤醒的（先被挂起先被唤醒）
 * 结论：
 *  notify 随机唤醒（非公平唤醒）
 *  singal 公平唤醒
 */
public class verifyFairAwaken {
    public static void main(String[] args) throws InterruptedException {
        NotifyTest.testNotify();
        SignalTest.testSingal();
    }

}
